package com.yupi.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yupi.usercenter.common.ErrorCode;
import com.yupi.usercenter.common.ResultUtils;
import com.yupi.usercenter.exception.BusinessException;
import com.yupi.usercenter.model.DTO.AuditFriendDTO;
import com.yupi.usercenter.model.DTO.MakeFriendDTO;
import com.yupi.usercenter.model.domain.FriendRelation;
import com.yupi.usercenter.model.domain.User;
import com.yupi.usercenter.service.UserService;
import com.yupi.usercenter.mapper.UserMapper;
import com.yupi.usercenter.utils.AlgorithmUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.math3.util.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.yupi.usercenter.contant.UserConstant.USER_LOGIN_STATE;

/**
 * 用户服务实现类
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://yupi.icu">编程导航知识星球</a>
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private FriendRelationServiceImpl friendRelationService;

    // https://www.code-nav.cn/

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "yupi";
    @Autowired
    private Gson gson;

    /**
     * 用户注册
     *
     * @param userAccount   用户账户
     * @param userPassword  用户密码
     * @param checkPassword 校验密码
     * @param planetCode    星球编号
     * @return 新用户 id
     */
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword, String planetCode) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, planetCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        if (planetCode.length() > 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号过长");
        }
        // 账户不能包含特殊字符
        String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
        if (matcher.find()) {
            return -1;
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            return -1;
        }
        // 账户不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
        }
        // 星球编号不能重复
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("planetCode", planetCode);
        count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "编号重复");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 3. 插入数据
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encryptPassword);
        user.setPlanetCode(planetCode);
        boolean saveResult = this.save(user);
        if (!saveResult) {
            return -1;
        }
        return user.getId();
    }

    // [加入星球](https://www.code-nav.cn/) 从 0 到 1 项目实战，经验拉满！10+ 原创项目手把手教程、7 日项目提升训练营、60+ 编程经验分享直播、1000+ 项目经验笔记

    /**
     * 用户登录
     *
     * @param userAccount  用户账户
     * @param userPassword 用户密码
     * @param request
     * @return 脱敏后的用户信息
     */
    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            return null;
        }
        if (userAccount.length() < 4) {
            return null;
        }
        if (userPassword.length() < 8) {
            return null;
        }
        // 账户不能包含特殊字符
        String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~！@#￥%……&*（）——+|{}【】'；：'。，、？]";
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
        if (matcher.find()) {
            return null;
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        // 用户不存在
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            return null;
        }
        // 3. 用户脱敏
        User safetyUser = getSafetyUser(user);
        
        // 4. 在Session中使用用户ID作为key的一部分，避免冲突
        String sessionKey = USER_LOGIN_STATE + ":" + safetyUser.getId();
        request.getSession().setAttribute(sessionKey, safetyUser);
        
        // 5. 将用户信息存储到Redis，使用用户ID作为key的一部分
        String redisKey = String.format("yupao:user:login:%d", safetyUser.getId());
        try {
            redisTemplate.opsForValue().set(redisKey, safetyUser, 24, TimeUnit.HOURS);
            log.info("用户 {} 登录成功，Session Key: {}, Redis缓存已设置", safetyUser.getUsername(), sessionKey);
        } catch (Exception e) {
            log.error("Redis缓存设置失败", e);
        }
        return safetyUser;
    }

    /**
     * 用户脱敏
     *
     * @param originUser
     * @return
     */
    @Override
    public User getSafetyUser(User originUser) {
        if (originUser == null) {
            return null;
        }
        User safetyUser = new User();
        safetyUser.setId(originUser.getId());
        safetyUser.setUsername(originUser.getUsername());
        safetyUser.setUserAccount(originUser.getUserAccount());
        safetyUser.setAvatarUrl(originUser.getAvatarUrl());
        safetyUser.setGender(originUser.getGender());
        safetyUser.setPhone(originUser.getPhone());
        safetyUser.setEmail(originUser.getEmail());
        safetyUser.setPlanetCode(originUser.getPlanetCode());
        safetyUser.setUserRole(originUser.getUserRole());
        safetyUser.setUserStatus(originUser.getUserStatus());
        safetyUser.setCreateTime(originUser.getCreateTime());
        safetyUser.setTags(originUser.getTags());
        return safetyUser;
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public int userLogout(HttpServletRequest request) {
        // 获取当前登录用户
        User currentUser = getLoginUser(request);
        if (currentUser != null) {
            // 清除Redis缓存
            String redisKey = String.format("yupao:user:login:%d", currentUser.getId());
            try {
                redisTemplate.delete(redisKey);
                log.info("用户 {} 登出成功，Redis缓存已清除", currentUser.getUsername());
            } catch (Exception e) {
                log.error("Redis缓存清除失败", e);
            }
        }
        // 清除当前用户的Session信息
        HttpSession session = request.getSession(false);
        if (session != null) {
            String sessionKey = USER_LOGIN_STATE + ":" + currentUser.getId();
            session.removeAttribute(sessionKey);
            log.info("用户 {} 的Session信息已清除: {}", currentUser.getUsername(), sessionKey);
        }
        return 1;
    }

    //根据标签查询用户
    @Override
    public List<User> searchUserTags(@RequestParam(required = false) List<String> tagList){
        if(CollectionUtils.isEmpty(tagList)){
           throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //sql  查询
        //拼接and 查询条件
        for (String tag : tagList) {
            queryWrapper.like("tags",tag);
        }
        List<User> userList = userMapper.selectList(queryWrapper);
        //用Java8特性，将用户列表中的每个用户进行脱敏，并收集到一个新的列表中。
        List<User> list = userList.stream().map(user -> getSafetyUser(user)).collect(Collectors.toList());

        return list;
    }

    /**
     * 获得当前登录用户信息
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 遍历Session中的所有属性，找到用户登录信息
        HttpSession session = request.getSession();
        User currentUser = null;
        
        // 查找所有以USER_LOGIN_STATE开头的属性
        for (String attributeName : Collections.list(session.getAttributeNames())) {
            if (attributeName.startsWith(USER_LOGIN_STATE + ":")) {
                Object userObj = session.getAttribute(attributeName);
                if (userObj instanceof User) {
                    currentUser = (User) userObj;
                    break;
                }
            }
        }
        
        // 如果当前用户为空，抛出未登录异常
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        
        // 获取当前用户的 ID
        long userId = currentUser.getId();
        
        // 先从Redis缓存中获取用户信息
        String redisKey = String.format("yupao:user:login:%d", userId);
        try {
            Object cachedUser = redisTemplate.opsForValue().get(redisKey);
            if (cachedUser != null) {
                log.info("从Redis缓存获取用户信息成功: {}", userId);
                return (User) cachedUser;
            }
        } catch (Exception e) {
            log.error("Redis缓存获取失败", e);
        }
        
        // Redis中没有，从数据库获取
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN, "用户不存在");
        }
        
        // 获取安全的用户信息
        User safetyUser = this.getSafetyUser(user);

        // 将安全的用户信息存入 Redis 缓存
        try {
            redisTemplate.opsForValue().set(redisKey, safetyUser, 24, TimeUnit.HOURS);
            log.info("用户信息已更新到Redis缓存: {}", userId);
        } catch (Exception e) {
            log.error("Redis缓存设置失败", e);
        }
        
        // 返回安全的用户信息
        return safetyUser;
    }

    /**
     * 获取最匹配的用户
     * @param num
     * @param loginUser
     * @return
     */
    @Override
    public List<User> matchUsers(long num, User loginUser) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "tags");
        queryWrapper.isNotNull("tags");
        List<User> userList = this.list(queryWrapper);
        String tags = loginUser.getTags();
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(tags, new TypeToken<List<String>>() {
        }.getType());
        // 用户列表的下标 => 相似度
        List<Pair<User, Long>> list = new ArrayList<>();
        // 依次计算所有用户和当前用户的相似度
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            String userTags = user.getTags();
            // 无标签或者为当前用户自己
            if (StringUtils.isBlank(userTags) || user.getId() == loginUser.getId()) {
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {
            }.getType());
            // 计算分数
            long distance = AlgorithmUtils.minDistance(tagList, userTagList);
            list.add(new Pair<>(user, distance));
        }
        // 按编辑距离由小到大排序
        List<Pair<User, Long>> topUserPairList = list.stream()
                .sorted((a, b) -> (int) (a.getValue() - b.getValue()))
                .limit(num)
                .collect(Collectors.toList());
        // 原本顺序的 userId 列表
        List<Long> userIdList = topUserPairList.stream().map(pair -> pair.getKey().getId()).collect(Collectors.toList());
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.in("id", userIdList);
        // 1, 3, 2
        // User1、User2、User3
        // 1 => User1, 2 => User2, 3 => User3
        Map<Long, List<User>> userIdUserListMap = this.list(userQueryWrapper)
                .stream()
                .map(user -> getSafetyUser(user))
                .collect(Collectors.groupingBy(User::getId));
        List<User> finalUserList = new ArrayList<>();
        for (Long userId : userIdList) {
            finalUserList.add(userIdUserListMap.get(userId).get(0));
        }
        return finalUserList;
    }

    @Override
    public String makeFriend(MakeFriendDTO makeFriendDTO, HttpServletRequest request) {
        //拿到登录用户
        User loginUser = getLoginUser(request);
        if(loginUser == null){
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        //判断是否已经添加
        QueryWrapper<FriendRelation> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId",loginUser.getId()).eq("friendId",makeFriendDTO.getUserId());
        if(friendRelationService.count(queryWrapper) > 0){
            throw new BusinessException(ErrorCode.FORBIDDEN,"已添加");
        }
        //插入用户关联表
        FriendRelation friendRelation = new FriendRelation();
        friendRelation.setUserId(loginUser.getId());  //登录用户
        friendRelation.setFriendId(makeFriendDTO.getUserId());   //待添加的好友
        friendRelation.setRelationStatus(0);
        friendRelation.setApplyMsg(makeFriendDTO.getMessage());
        friendRelationService.save(friendRelation);


        return "已发送好友申请";
    }

    @Override
    public String auditFriend(AuditFriendDTO auditFriendDTO, HttpServletRequest request) {
        if (auditFriendDTO == null) {
            return "参数错误";
        }

        User loginUser = getLoginUser(request);
        if (loginUser == null) {
            return "未登录";
        }

        if (auditFriendDTO.getApplyUserId() == null) {
            return "参数错误";
        }

        // 查询好友申请记录
        QueryWrapper<FriendRelation> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("friendId", loginUser.getId());
        queryWrapper.eq("userId",auditFriendDTO.getApplyUserId());
        FriendRelation friendRelation = friendRelationService.getOne(queryWrapper);

        if (friendRelation == null || friendRelation.getRelationStatus() != 0) {
            return "不允许审核";
        }

        if (Objects.equals(auditFriendDTO.getAuditResult(), "1")) {
            // 同意好友申请
            friendRelation.setRelationStatus(1);
            friendRelationService.updateById(friendRelation);

            // 插入反向记录：申请人也把当前用户加为好友
            FriendRelation reverseRelation = new FriendRelation();
            reverseRelation.setUserId(auditFriendDTO.getApplyUserId());  // 申请人
            reverseRelation.setFriendId(loginUser.getId());              // 当前用户
            reverseRelation.setRelationStatus(1);                        // 已通过
            reverseRelation.setRemark(friendRelation.getRemark());       // 复制备注
            friendRelationService.save(reverseRelation);

            return "好友申请已通过";
        }

        if (Objects.equals(auditFriendDTO.getAuditResult(), "2")) {
            // 拒绝好友申请
            friendRelation.setRelationStatus(2);
            friendRelationService.updateById(friendRelation);

            return "好友申请已拒绝";
        }

        return "处理完成";
    }

    @Override
    public List<User> myFriend(String relationStatus, HttpServletRequest request) {
        User loginUser = this.getLoginUser(request);

        // 查询用户作为发起方或接收方的好友关系
        List<FriendRelation> list = friendRelationService.selectFriends(loginUser.getId(), relationStatus);

        List<User> users = list.stream().map(friendRelation -> {
            // 根据关系记录确定好友ID
            Long friendId = friendRelation.getUserId().equals(loginUser.getId())
                    ? friendRelation.getFriendId()
                    : friendRelation.getUserId();
            User user = this.getById(friendId);
            return this.getSafetyUser(user);  // 返回脱敏后的用户信息
        }).collect(Collectors.toList());

        return users;
    }

    @Override
    public Page<User> searchUser(String keyWord, long pageNum, long pageSize) {
        // 计算偏移量
        long offset = (pageNum - 1) * pageSize;

        // 执行查询
        List<User> userList = baseMapper.searchUser(keyWord, offset, pageSize);

        // 查询总数
        long total = baseMapper.searchUserCount(keyWord);

        // 创建Page对象
        Page<User> page = new Page<>(pageNum, pageSize);
        page.setRecords(userList.stream().map(this::getSafetyUser).collect(Collectors.toList()));
        page.setTotal(total);

        return page;
    }
}
